#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Name : __init__.py
# Author: ls_seraph@126.com
# Time  : 2021/3/6 22:46

import io
import time
import unittest
import sys
from frame.common.util.log_utils import logger as log


# ------------------------------------------------------------------------
# 下面的重定向器用于在测试期间捕获输出。发送到sys.stdout和sys.stderr的输出将被自动捕获。
# 但是在某些情况下，在调用HTMLTestRunner之前（例如，调用logging.basicConfig）sys.stdout已被缓存。
# 为了捕获这些输出，请对高速缓存的流使用重定向器。
# e.g.
#   >>> logging.basicConfig(stream=HTMLTestRunner.stdout_redirector)
#   >>>

# 输出 或 错误 重定向标准
class OutputRedirector(object):
    """ Wrapper to redirect stdout or stderr """

    def __init__(self, fp):
        self.fp = fp

    def write(self, s):
        self.fp.write(s)

    def writelines(self, lines):
        self.fp.writelines(lines)

    def flush(self):
        self.fp.flush()


stdout_redirector = OutputRedirector(sys.stdout)
stderr_redirector = OutputRedirector(sys.stderr)

# 测试结果管理
TestResult = unittest.TestResult


# 加强 unittest.TestResult 的类
# 增加了变量 success_count、failure_count、error_count 用例成功数、失败数、错误数
class _TestResult(TestResult):

    # 初始化
    def __init__(self, verbosity=1):
        TestResult.__init__(self)
        self.verbosity = verbosity  # 是否输出用例test？
        # 定义输出IO
        self.stdout0 = None
        self.stderr0 = None
        # result is a list of result in 4 tuple
        # (
        #   result code :执行情况(0: success; 1: fail; 2: error),
        #   TestCase object:用例对象(执行类、方法)
        #   Test output (byte string):输出信息
        #   stack trace :异常信息
        #   use time: 耗时
        # )
        self.all_result = []  # 返回全部用例情况
        self.success_count = 0  # 成功数
        self.fail_count = 0  # 失败数
        self.error_count = 0  # 错误数
        self.fail_case = []  # 返回失败合集
        self.error_case = []  # 返回错误合集
        # 计算单个方法时间差
        self.test_start_time = None
        self.test_end_time = None
        # 开始结束时间
        self.all_start_time = ""
        self.all_end_time = ""
        self.all_use_time = round(0, 2)  # 耗时

    def startTest(self, test):
        # 启动测试
        TestResult.startTest(self, test)
        # just one buffer for both stdout and stderr
        self.outputBuffer = io.StringIO()
        stdout_redirector.fp = self.outputBuffer
        stderr_redirector.fp = self.outputBuffer
        self.stdout0 = sys.stdout
        self.stderr0 = sys.stderr
        sys.stdout = stdout_redirector
        sys.stderr = stderr_redirector
        self.test_start_time = round(time.time(), 2)

    # 断开输出
    def complete_output(self):
        """
        Disconnect output redirection and return buffer.
        Safe to call multiple times.
        """
        self.test_end_time = round(time.time(), 2)
        if self.stdout0:
            sys.stdout = self.stdout0
            sys.stderr = self.stderr0
            self.stdout0 = None
            self.stderr0 = None
        return self.outputBuffer.getvalue()

    # 停止测试
    def stopTest(self, test):
        # Usually one of addSuccess, addError or addFailure would have been called.
        # But there are some path in unittest that would bypass this.
        # We must disconnect stdout in stopTest(), which is guaranteed to be called.
        self.complete_output()

    # 添加成功信息
    def addSuccess(self, test):
        self.success_count += 1
        TestResult.addSuccess(self, test)
        output = self.complete_output()
        log.info(str(test) + "成功")
        use_time = round(self.test_end_time - self.test_start_time, 2)
        self.all_result.append((0, test, output, 'Success', use_time))
        if self.verbosity > 1:
            sys.stderr.write('【成功】')
            sys.stderr.write(str(test))
            sys.stderr.write('\n')
        else:
            sys.stderr.write('【成功】')
            sys.stderr.write('\n')

    # 添加失败信息
    def addError(self, test, err):
        self.error_count += 1
        TestResult.addError(self, test, err)
        _, _exc_str = self.errors[-1]
        output = self.complete_output()
        log.error(_exc_str)
        use_time = round(self.test_end_time - self.test_start_time, 2)
        self.all_result.append((2, test, output, _exc_str, use_time))
        self.error_case.append(str(test))  # 收集错误用例名称
        if self.verbosity > 1:
            sys.stderr.write('【错误】')
            sys.stderr.write(str(test))
            sys.stderr.write('\n')
        else:
            sys.stderr.write('【错误】')
            sys.stderr.write('\n')

    # 添加错误信息
    def addFailure(self, test, err):
        self.fail_count += 1
        TestResult.addFailure(self, test, err)
        _, _exc_str = self.failures[-1]
        output = self.complete_output()
        log.warn(_exc_str)

        use_time = round(self.test_end_time - self.test_start_time, 2)
        self.all_result.append((1, test, output, _exc_str, use_time))
        self.fail_case.append(str(test))  # 收集失败用例名称
        if self.verbosity > 1:
            sys.stderr.write('【失败】')
            sys.stderr.write(str(test))
            sys.stderr.write('\n')
        else:
            sys.stderr.write('【失败】')
            sys.stderr.write('\n')


# 自定义测试执行器
class TestRunner():
    # 这里可以说是使用 HTMLTestRunner.py 的入口。定义了多个我们可以看到的方法，比如 run(self, test)
    def __init__(self, verbosity=2, title=None, description=None, tester=None):
        """
        :param stream: 新建报告文件流
        :param verbosity: 1-只输出成功与否,2-会输出用例名称 默认2 输出用例名
        """
        # 是否需要截图 need_screenshot 参数，-1为无需截图，否则需要截图
        self.need_screenshot = 0
        self.verbosity = verbosity
        self.startTime = time.time()

    def run(self, test):
        # 装配结果收集器
        result = _TestResult(self.verbosity)  # verbosity为1,只输出成功与否，为2会输出用例名称
        # 执行用例集 结果收集到result
        log.info("【测试执行开始】")
        test(result)
        result.all_start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.startTime))
        result.all_end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        result.all_use_time = round(round(time.time() - round(self.startTime, 2), 2), 2)
        log.info("【测试执行结束】")
        log.info("合计耗时: %s" % result.all_use_time)
        # print("------------- 合计耗时: %s -------------" % result.all_use_time, file=sys.stderr)
        return result
